﻿// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

// Original file: https://github.com/IdentityServer/IdentityServer4.Samples
// Modified by 

using System;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using IdentityModel;
using IdentityServer4;
using IdentityServer4.Events;
using IdentityServer4.Extensions;
using IdentityServer4.Models;
using IdentityServer4.Services;
using IdentityServer4.Stores;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using Idsrv4.Admin.Shared.Configuration.Configuration.Identity;
using Idsrv4.Admin.STS.Identity.Configuration;
using Idsrv4.Admin.STS.Identity.Helpers;
using Idsrv4.Admin.STS.Identity.Helpers.Localization;
using Idsrv4.Admin.STS.Identity.ViewModels.Account;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace Idsrv4.Admin.STS.Identity.Controllers;

[SecurityHeaders]
[Authorize]
public class AccountController<TUser, TKey> : Controller
    where TUser : IdentityUser<TKey>, new()
    where TKey : IEquatable<TKey>
{
    private readonly IClientStore _clientStore;
    private readonly IEmailSender _emailSender;
    private readonly IEventService _events;
    private readonly IdentityOptions _identityOptions;
    private readonly IIdentityServerInteractionService _interaction;
    private readonly IGenericControllerLocalizer<AccountController<TUser, TKey>> _localizer;
    private readonly ILogger<AccountController<TUser, TKey>> _logger;
    private readonly LoginConfiguration _loginConfiguration;
    private readonly RegisterConfiguration _registerConfiguration;
    private readonly IAuthenticationSchemeProvider _schemeProvider;
    private readonly ApplicationSignInManager<TUser> _signInManager;
    private readonly UserManager<TUser> _userManager;
    private readonly UserResolver<TUser> _userResolver;

    public AccountController(
        UserResolver<TUser> userResolver,
        UserManager<TUser> userManager,
        ApplicationSignInManager<TUser> signInManager,
        IIdentityServerInteractionService interaction,
        IClientStore clientStore,
        IAuthenticationSchemeProvider schemeProvider,
        IEventService events,
        IEmailSender emailSender,
        IGenericControllerLocalizer<AccountController<TUser, TKey>> localizer,
        LoginConfiguration loginConfiguration,
        RegisterConfiguration registerConfiguration,
        IdentityOptions identityOptions,
        ILogger<AccountController<TUser, TKey>> logger)
    {
        _userResolver = userResolver;
        _userManager = userManager;
        _signInManager = signInManager;
        _interaction = interaction;
        _clientStore = clientStore;
        _schemeProvider = schemeProvider;
        _events = events;
        _emailSender = emailSender;
        _localizer = localizer;
        _loginConfiguration = loginConfiguration;
        _registerConfiguration = registerConfiguration;
        _identityOptions = identityOptions;
        _logger = logger;
    }

    /// <summary>
    ///     Entry point into the login workflow
    /// </summary>
    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> Login(string returnUrl)
    {
        // build a model so we know what to show on the login page
        var vm = await BuildLoginViewModelAsync(returnUrl);

        if (vm.EnableLocalLogin == false && vm.ExternalProviders.Count() == 1)
            // only one option for logging in
            return ExternalLogin(vm.ExternalProviders.First().AuthenticationScheme, returnUrl);

        return View(vm);
    }

    /// <summary>
    ///     Handle postback from username/password login
    /// </summary>
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(LoginInputModel model, string button)
    {
        // check if we are in the context of an authorization request
        var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);

        // the user clicked the "cancel" button
        if (button != "login")
        {
            if (context != null)
            {
                // if the user cancels, send a result back into IdentityServer as if they 
                // denied the consent (even if this client does not require consent).
                // this will send back an access denied OIDC error response to the client.
                await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied);

                // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
                if (context.IsNativeClient())
                    // The client is native, so this change in how to
                    // return the response is for better UX for the end user.
                    return this.LoadingPage("Redirect", model.ReturnUrl);

                return Redirect(model.ReturnUrl);
            }

            // since we don't have a valid context, then we just go back to the home page
            return Redirect("~/");
        }

        if (ModelState.IsValid)
        {
            var user = await _userResolver.GetUserAsync(model.Username);
            if (user != default(TUser))
            {
                var result =
                    await _signInManager.PasswordSignInAsync(user.UserName, model.Password, model.RememberLogin, true);
                if (result.Succeeded)
                {
                    await _events.RaiseAsync(
                        new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName));

                    if (context != null)
                    {
                        if (context.IsNativeClient())
                            // The client is native, so this change in how to
                            // return the response is for better UX for the end user.
                            return this.LoadingPage("Redirect", model.ReturnUrl);

                        // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
                        return Redirect(model.ReturnUrl);
                    }

                    // request for a local page
                    if (Url.IsLocalUrl(model.ReturnUrl)) return Redirect(model.ReturnUrl);

                    if (string.IsNullOrEmpty(model.ReturnUrl)) return Redirect("~/");

                    // user might have clicked on a malicious link - should be logged
                    throw new Exception("invalid return URL");
                }

                if (result.RequiresTwoFactor)
                    return RedirectToAction(nameof(LoginWith2fa),
                        new { model.ReturnUrl, RememberMe = model.RememberLogin });

                if (result.IsLockedOut) return View("Lockout");
            }

            await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials",
                clientId: context?.Client.ClientId));
            ModelState.AddModelError(string.Empty, _localizer["InvalidCredentialsErrorMessage"]);
        }

        // something went wrong, show form with error
        var vm = await BuildLoginViewModelAsync(model);
        return View(vm);
    }


    /// <summary>
    ///     Show logout page
    /// </summary>
    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> Logout(string logoutId)
    {
        // build a model so the logout page knows what to display
        var vm = await BuildLogoutViewModelAsync(logoutId);

        if (vm.ShowLogoutPrompt == false)
            // if the request for logout was properly authenticated from IdentityServer, then
            // we don't need to show the prompt and can just log the user out directly.
            return await Logout(vm);

        return View(vm);
    }

    /// <summary>
    ///     Handle logout page postback
    /// </summary>
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Logout(LogoutInputModel model)
    {
        // build a model so the logged out page knows what to display
        var vm = await BuildLoggedOutViewModelAsync(model.LogoutId);

        if (User?.Identity.IsAuthenticated == true)
        {
            // delete local authentication cookie
            await _signInManager.SignOutAsync();

            // raise the logout event
            await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
        }

        // check if we need to trigger sign-out at an upstream identity provider
        if (vm.TriggerExternalSignout)
        {
            // build a return URL so the upstream provider will redirect back
            // to us after the user has logged out. this allows us to then
            // complete our single sign-out processing.
            var url = Url.Action("Logout", new { logoutId = vm.LogoutId });

            // this triggers a redirect to the external provider for sign-out
            return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme);
        }

        return View("LoggedOut", vm);
    }

    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> ConfirmEmail(string userId, string code)
    {
        if (userId == null || code == null) return View("Error");
        var user = await _userManager.FindByIdAsync(userId);
        if (user == null) return View("Error");

        code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));

        var result = await _userManager.ConfirmEmailAsync(user, code);
        return View(result.Succeeded ? "ConfirmEmail" : "Error");
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult ForgotPassword() => View(new ForgotPasswordViewModel());

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
    {
        if (ModelState.IsValid)
        {
            TUser user = null;
            switch (model.Policy)
            {
                case LoginResolutionPolicy.Email:
                    try
                    {
                        user = await _userManager.FindByEmailAsync(model.Email);
                    }
                    catch (Exception ex)
                    {
                        // in case of multiple users with the same email this method would throw and reveal that the email is registered
                        _logger.LogError("Error retrieving user by email ({0}) for forgot password functionality: {1}",
                            model.Email, ex.Message);
                        user = null;
                    }

                    break;
                case LoginResolutionPolicy.Username:
                    try
                    {
                        user = await _userManager.FindByNameAsync(model.Username);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(
                            "Error retrieving user by userName ({0}) for forgot password functionality: {1}",
                            model.Username, ex.Message);
                        user = null;
                    }

                    break;
            }

            if (user == null || !await _userManager.IsEmailConfirmedAsync(user))
                // Don't reveal that the user does not exist
                return View("ForgotPasswordConfirmation");

            var code = await _userManager.GeneratePasswordResetTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code },
                HttpContext.Request.Scheme);

            await _emailSender.SendEmailAsync(user.Email, _localizer["ResetPasswordTitle"],
                _localizer["ResetPasswordBody", HtmlEncoder.Default.Encode(callbackUrl)]);

            return View("ForgotPasswordConfirmation");
        }

        return View(model);
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult ResetPassword(string code = null) => code == null ? View("Error") : View();

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
    {
        if (!ModelState.IsValid) return View(model);
        var user = await _userManager.FindByEmailAsync(model.Email);
        if (user == null)
            // Don't reveal that the user does not exist
            return RedirectToAction(nameof(ResetPasswordConfirmation), "Account");

        var code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(model.Code));
        var result = await _userManager.ResetPasswordAsync(user, code, model.Password);

        if (result.Succeeded) return RedirectToAction(nameof(ResetPasswordConfirmation), "Account");

        AddErrors(result);

        return View();
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult ResetPasswordConfirmation() => View();

    [HttpGet]
    [AllowAnonymous]
    public IActionResult ForgotPasswordConfirmation() => View();

    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
    {
        if (remoteError != null)
        {
            ModelState.AddModelError(string.Empty, _localizer["ErrorExternalProvider", remoteError]);

            return View(nameof(Login));
        }

        var info = await _signInManager.GetExternalLoginInfoAsync();
        if (info == null) return RedirectToAction(nameof(Login));

        // Sign in the user with this external login provider if the user already has a login.
        var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, false);
        if (result.Succeeded) return RedirectToLocal(returnUrl);
        if (result.RequiresTwoFactor) return RedirectToAction(nameof(LoginWith2fa), new { ReturnUrl = returnUrl });
        if (result.IsLockedOut) return View("Lockout");

        // If the user does not have an account, then ask the user to create an account.
        ViewData["ReturnUrl"] = returnUrl;
        ViewData["LoginProvider"] = info.LoginProvider;
        var email = info.Principal.FindFirstValue(ClaimTypes.Email);
        var userName = info.Principal.Identity.Name;

        return View("ExternalLoginConfirmation",
            new ExternalLoginConfirmationViewModel { Email = email, UserName = userName });
    }

    [HttpPost]
    [HttpGet]
    [AllowAnonymous]
    public IActionResult ExternalLogin(string provider, string returnUrl = null)
    {
        // Request a redirect to the external login provider.
        var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
        var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);

        return Challenge(properties, provider);
    }

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model,
        string returnUrl = null)
    {
        returnUrl = returnUrl ?? Url.Content("~/");

        // Get the information about the user from the external login provider
        var info = await _signInManager.GetExternalLoginInfoAsync();
        if (info == null) return View("ExternalLoginFailure");

        if (ModelState.IsValid)
        {
            var user = new TUser
            {
                UserName = model.UserName,
                Email = model.Email
            };

            var result = await _userManager.CreateAsync(user);
            if (result.Succeeded)
            {
                result = await _userManager.AddLoginAsync(user, info);
                if (result.Succeeded)
                {
                    await _signInManager.SignInAsync(user, false);

                    return RedirectToLocal(returnUrl);
                }
            }

            AddErrors(result);
        }

        ViewData["LoginProvider"] = info.LoginProvider;
        ViewData["ReturnUrl"] = returnUrl;

        return View(model);
    }

    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> LoginWithRecoveryCode(string returnUrl = null)
    {
        // Ensure the user has gone through the username & password screen first
        var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
        if (user == null) throw new InvalidOperationException(_localizer["Unable2FA"]);

        var model = new LoginWithRecoveryCodeViewModel
        {
            ReturnUrl = returnUrl
        };

        return View(model);
    }

    [HttpPost]
    [AllowAnonymous]
    public async Task<IActionResult> LoginWithRecoveryCode(LoginWithRecoveryCodeViewModel model)
    {
        if (!ModelState.IsValid) return View(model);

        var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
        if (user == null) throw new InvalidOperationException(_localizer["Unable2FA"]);

        var recoveryCode = model.RecoveryCode.Replace(" ", string.Empty);

        var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);

        if (result.Succeeded) return LocalRedirect(string.IsNullOrEmpty(model.ReturnUrl) ? "~/" : model.ReturnUrl);

        if (result.IsLockedOut) return View("Lockout");

        ModelState.AddModelError(string.Empty, _localizer["InvalidRecoveryCode"]);

        return View(model);
    }

    [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> LoginWith2fa(bool rememberMe, string returnUrl = null)
    {
        // Ensure the user has gone through the username & password screen first
        var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();

        if (user == null) throw new InvalidOperationException(_localizer["Unable2FA"]);

        var model = new LoginWith2faViewModel
        {
            ReturnUrl = returnUrl,
            RememberMe = rememberMe
        };

        return View(model);
    }

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> LoginWith2fa(LoginWith2faViewModel model)
    {
        if (!ModelState.IsValid) return View(model);

        var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
        if (user == null) throw new InvalidOperationException(_localizer["Unable2FA"]);

        var authenticatorCode = model.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty);

        var result =
            await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, model.RememberMe,
                model.RememberMachine);

        if (result.Succeeded) return LocalRedirect(string.IsNullOrEmpty(model.ReturnUrl) ? "~/" : model.ReturnUrl);

        if (result.IsLockedOut) return View("Lockout");

        ModelState.AddModelError(string.Empty, _localizer["InvalidAuthenticatorCode"]);

        return View(model);
    }

    [HttpGet]
    [AllowAnonymous]
    public IActionResult Register(string returnUrl = null)
    {
        if (!_registerConfiguration.Enabled) return View("RegisterFailure");

        ViewData["ReturnUrl"] = returnUrl;

        return _loginConfiguration.ResolutionPolicy switch
        {
            LoginResolutionPolicy.Username => View(),
            LoginResolutionPolicy.Email => View("RegisterWithoutUsername"),
            _ => View("RegisterFailure")
        };
    }

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null,
        bool IsCalledFromRegisterWithoutUsername = false)
    {
        if (!_registerConfiguration.Enabled) return View("RegisterFailure");

        returnUrl ??= Url.Content("~/");

        ViewData["ReturnUrl"] = returnUrl;

        if (!ModelState.IsValid) return View(model);

        var user = new TUser
        {
            UserName = model.UserName,
            Email = model.Email
        };

        var result = await _userManager.CreateAsync(user, model.Password);
        if (result.Succeeded)
        {
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code },
                HttpContext.Request.Scheme);

            await _emailSender.SendEmailAsync(model.Email, _localizer["ConfirmEmailTitle"],
                _localizer["ConfirmEmailBody", HtmlEncoder.Default.Encode(callbackUrl)]);

            if (_identityOptions.SignIn.RequireConfirmedAccount) return View("RegisterConfirmation");

            await _signInManager.SignInAsync(user, false);
            return LocalRedirect(returnUrl);
        }

        AddErrors(result);

        // If we got this far, something failed, redisplay form
        if (IsCalledFromRegisterWithoutUsername)
        {
            var registerWithoutUsernameModel = new RegisterWithoutUsernameViewModel
            {
                Email = model.Email,
                Password = model.Password,
                ConfirmPassword = model.ConfirmPassword
            };

            return View("RegisterWithoutUsername", registerWithoutUsernameModel);
        }

        return View(model);
    }

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> RegisterWithoutUsername(RegisterWithoutUsernameViewModel model,
        string returnUrl = null)
    {
        var registerModel = new RegisterViewModel
        {
            UserName = model.Email,
            Email = model.Email,
            Password = model.Password,
            ConfirmPassword = model.ConfirmPassword
        };

        return await Register(registerModel, returnUrl, true);
    }

    /*****************************************/
    /* helper APIs for the AccountController */
    /*****************************************/
    private IActionResult RedirectToLocal(string returnUrl)
    {
        if (Url.IsLocalUrl(returnUrl)) return Redirect(returnUrl);

        return RedirectToAction(nameof(HomeController.Index), "Home");
    }

    private void AddErrors(IdentityResult result)
    {
        foreach (var error in result.Errors) ModelState.AddModelError(string.Empty, error.Description);
    }

    private async Task<LoginViewModel> BuildLoginViewModelAsync(string returnUrl)
    {
        var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
        if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null)
        {
            var local = context.IdP == IdentityServerConstants.LocalIdentityProvider;

            // this is meant to short circuit the UI and only trigger the one external IdP
            var vm = new LoginViewModel
            {
                EnableLocalLogin = local,
                ReturnUrl = returnUrl,
                Username = context?.LoginHint
            };

            if (!local) vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } };

            return vm;
        }

        var schemes = await _schemeProvider.GetAllSchemesAsync();

        var providers = schemes
            .Where(x => x.DisplayName != null)
            .Select(x => new ExternalProvider
            {
                DisplayName = x.DisplayName ?? x.Name,
                AuthenticationScheme = x.Name
            }).ToList();

        var allowLocal = true;
        if (context?.Client.ClientId != null)
        {
            var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId);
            if (client != null)
            {
                allowLocal = client.EnableLocalLogin;

                if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any())
                    providers = providers.Where(provider
                        => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList();
            }
        }

        return new LoginViewModel
        {
            AllowRememberLogin = AccountOptions.AllowRememberLogin,
            EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin,
            ReturnUrl = returnUrl,
            Username = context?.LoginHint,
            ExternalProviders = providers.ToArray()
        };
    }

    private async Task<LoginViewModel> BuildLoginViewModelAsync(LoginInputModel model)
    {
        var vm = await BuildLoginViewModelAsync(model.ReturnUrl);
        vm.Username = model.Username;
        vm.RememberLogin = model.RememberLogin;
        return vm;
    }

    private async Task<LogoutViewModel> BuildLogoutViewModelAsync(string logoutId)
    {
        var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt };

        if (User?.Identity.IsAuthenticated != true)
        {
            // if the user is not authenticated, then just show logged out page
            vm.ShowLogoutPrompt = false;
            return vm;
        }

        var context = await _interaction.GetLogoutContextAsync(logoutId);
        if (context?.ShowSignoutPrompt == false)
        {
            // it's safe to automatically sign-out
            vm.ShowLogoutPrompt = false;
            return vm;
        }

        // show the logout prompt. this prevents attacks where the user
        // is automatically signed out by another malicious web page.
        return vm;
    }

    private async Task<LoggedOutViewModel> BuildLoggedOutViewModelAsync(string logoutId)
    {
        // get context information (client name, post logout redirect URI and iframe for federated signout)
        var logout = await _interaction.GetLogoutContextAsync(logoutId);

        var vm = new LoggedOutViewModel
        {
            AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut,
            PostLogoutRedirectUri = logout?.PostLogoutRedirectUri,
            ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName,
            SignOutIframeUrl = logout?.SignOutIFrameUrl,
            LogoutId = logoutId
        };

        if (User?.Identity.IsAuthenticated == true)
        {
            var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value;
            if (idp != null && idp != IdentityServerConstants.LocalIdentityProvider)
            {
                var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp);
                if (providerSupportsSignout)
                {
                    if (vm.LogoutId == null)
                        // if there's no current logout context, we need to create one
                        // this captures necessary info from the current logged in user
                        // before we signout and redirect away to the external IdP for signout
                        vm.LogoutId = await _interaction.CreateLogoutContextAsync();

                    vm.ExternalAuthenticationScheme = idp;
                }
            }
        }

        return vm;
    }
}